The symmetry-exceptional slopes of K=o9_30634 are (-2,1), (-1,1), and (2,1). All three have symmetry group Z/2+Z/2 and thus they can be written in three different ways as DBC. Here we determine all these three ways. Since K is strongly invertible all slopes can be written in one way as DBC branched along a link L in S3. That means for the three symmetry-exceptional fillings we need to find another 2 ways to write them as DBCs.
import time
import snappy
import pandas
DBChomologies = pandas.read_csv("DBChomologies_non_alternating.csv") # We load a list of all non alternating links in
#the HTLinkExteriors together with their homologies (if the homology is cyclic) and their crossing numbers.
### REMARK: To build the list we need to run the code in the file Build_DBChomologies which takes around 60 minutes.
#Here we just load it.
def double_branched_cover(link):
"""
Returns the double branched cover of the link.
"""
L=link.copy()
for i in range(L.num_cusps()):
L.dehn_fill((2,0),i)
for cov in L.covers(2):
if (2.0, 0.0) not in cov.cusp_info('filling'):
return cov
def better_is_isometric_to(X,Y,index):
"""
Returns True if X and Y are isometric.
Returns False if X and Y have different homologies. TO DO: Use volume to rigorously distinguish X and Y.
Returns 'unclear' if SnapPy cannot verify it.
The higher the index the harder SnapPy tries.
"""
w='unclear'
if X.homology()!=Y.homology():
w=False
if w=='unclear':
for i in (0,index):
try:
w=X.is_isometric_to(Y)
except RuntimeError:
pass
except snappy.SnapPeaFatalError:
pass
if w==True:
break
if w==False:
w='unclear'
X.randomize()
Y.randomize()
i=i+1
return w
def possible_DBC(homologies,max_crossings=15):
"""
Takes a list of orders of homologies and returns a list consisting of all DBC of alternating links in the HT link table with that homologies together with the link names.
"""
DBCList=[]
LINKS=[]
for order in homologies:
LINKS=LINKS+DBChomologies.loc[(DBChomologies['homology']==order) & (DBChomologies['crossings']<=max_crossings)]['knot'].tolist()
for link in LINKS:
L=snappy.Manifold(link)
D=double_branched_cover(L)
DBCList.append([D,link])
return DBCList
### The following two functions are written by Dunfield and search for positive triangulations.
def all_positive(manifold):
return manifold.solution_type() == 'all tetrahedra positively oriented'
def find_positive_triangulation(manifold, tries=100):
M = manifold.copy()
for i in range(tries):
if all_positive(M):
return M
M.randomize()
for d in M.dual_curves():
X = M.drill(d)
X = X.filled_triangulation()
X.dehn_fill((1,0))
for i in range(tries):
if all_positive(X):
return X
X.randomize()
# In the closed case, here is another trick.
if all(not c for c in M.cusp_info('is_complete')):
for i in range(tries):
# Drills out a random edge
X = M.__class__(M.filled_triangulation())
if all_positive(X):
return X
M.randomize()
def better_find_positive_triangulation(M,tries=1):
'''
Search for a positive triangulation, but ignores errors.
'''
RandomizeCount=0
while RandomizeCount<tries:
try:
X=find_positive_triangulation(M)
return X
except snappy.SnapPeaFatalError:
M.randomize()
RandomizeCount=RandomizeCount+1
return None
def is_alternating(knot,slope,try_hard=False,index=10,tries=1,max_cro=15):
'''
Checks if the slope is alternating.
'''
K=snappy.Manifold(knot)
K.dehn_fill(slope)
DBC=possible_DBC([K.homology().order()],max_crossings=max_cro)
for D in DBC:
w=better_is_isometric_to(D[0],K,index)
if w==True:
return [slope,D[1]]
if try_hard:
X=better_find_positive_triangulation(K,tries)
if X is not None:
for D in DBC:
Y=better_find_positive_triangulation(D[0],tries)
if Y is not None:
w=better_is_isometric_to(X,Y,index)
if w==True:
return [slope,D[1]]
return False
K=snappy.Manifold('o9_30634')
exc_sym_slopes=[(-2,1),(-1,1),(2,1)]
for s in exc_sym_slopes:
K.dehn_fill(s)
print(s,K.symmetry_group())
(-2, 1) Z/2 + Z/2 (-1, 1) Z/2 + Z/2 (2, 1) Z/2 + Z/2
start_time = time.time()
branching_sets=[]
knot='o9_30634'
for slope in exc_sym_slopes:
w=is_alternating(knot,slope,index=25,max_cro=15)
if w!=False:
branching_sets.append([knot,w[0],w[1]])
print('We found a branching set:',knot,slope,w[1])
print('Total number of branching sets we have found:', len(branching_sets))
print('Total time taken: %s minutes ---' % ((time.time() - start_time)/60))
We found a branching set: o9_30634 (-1, 1) K13n2148 Total number of branching sets we have found: 1 Total time taken: 0.8800097187360127 minutes ---
Next we will search for branching sets in more general manifolds. For that we will take links in the HT link tables fill some one component of it to get a surgery diagram of a link in a manifold. Then we take the double branched cover of that link and search for a match with a symmetric filling on A.
DBChomologies_branching = pandas.read_csv("DBChomologies_one_filling.csv")
### REMARK: To build the list we need to run the code in the file Build_DBChomologies which takes around 3 days. Here we just load it.
def double_branched_cover(link):
"""
Returns the double branched covers of the link. This works also for links in a more general manifold.
Note that a knot in a more general manifold may have more than one double branched cover
(or no double branched cover at all if the knot represents a primitive element in homology).
This function will return the complete list of all double branched covers of the link.
"""
L=link.copy()
for i in range(L.num_cusps()):
if L.cusp_info(i).filling==(0.0, 0.0):
L.dehn_fill((2,0),i)
return [cov for cov in L.covers(2) if (2.0, 0.0) not in cov.cusp_info('filling')]
def possible_DBC_surgery_diagrams(homologies,max_crossings=15):
"""
Reads off the possible surgery diagrams.
"""
DBCList=[]
LINKS=[]
CUSPS=[]
SLOPES_STRINGS=[]
for order in homologies:
LINKS=LINKS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['knot'].tolist()
CUSPS=CUSPS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['cusp'].tolist()
SLOPES_STRINGS=SLOPES_STRINGS+DBChomologies_branching.loc[(DBChomologies_branching['homology']==order) & (DBChomologies_branching['crossings']<=max_crossings)]['filling'].tolist()
SLOPES=[]
for string in SLOPES_STRINGS:
string_without_brackets=string[1:-1]
SLOPES.append(tuple(map(int, string_without_brackets.split(', '))))
for i in range(0,len(LINKS)):
DBCList.append([LINKS[i],SLOPES[i],CUSPS[i]])
return DBCList
def search_for_branching_set(knot,slope,try_hard=False,index=10,tries=1,max_crossings=15):
'''
Searchs for a surgery diagram of the rbanching set
'''
K=snappy.Manifold(knot)
K.dehn_fill(slope)
DBC=possible_DBC_surgery_diagrams([K.homology().order()],max_crossings)
for DIAGRAM in DBC:
L=snappy.Manifold(DIAGRAM[0])
L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
for D in double_branched_cover(L):
w=better_is_isometric_to(D,K,index)
if w==True:
return [[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
if try_hard:
X=better_find_positive_triangulation(K,tries)
if X is not None:
for DIAGRAM in DBC:
L=snappy.Manifold(DIAGRAM[0])
L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
for D in double_branched_cover(L):
Y=better_find_positive_triangulation(D,tries)
if Y is not None:
w=better_is_isometric_to(X,Y,index)
if w==True:
return [[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
return False
def search_for_three_branching_sets(knot,slope,try_hard=False,index=10,tries=1,max_crossings=15):
'''
Searchs for a surgery diagram of the branching set
'''
BRANCHING_SETS=[]
K=snappy.Manifold(knot)
K.dehn_fill(slope)
homologies=[]
DBC=possible_DBC_surgery_diagrams([K.homology().order()],max_crossings)
for DIAGRAM in DBC:
L=snappy.Manifold(DIAGRAM[0])
L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
for D in double_branched_cover(L):
w=better_is_isometric_to(D,K,index)
if w==True:
BRANCHING_SETS=BRANCHING_SETS+[[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
for i in range(L.num_cusps()):
if L.cusp_info(i).filling==(0.0, 0.0):
L.dehn_fill((1,0),i)
if L.homology().order() not in homologies:
homologies.append(L.homology().order())
if len(homologies)==3:
return BRANCHING_SETS
if try_hard:
X=better_find_positive_triangulation(K,tries)
if X is not None:
for DIAGRAM in DBC:
L=snappy.Manifold(DIAGRAM[0])
L.dehn_fill(DIAGRAM[1],DIAGRAM[2])
for D in double_branched_cover(L):
Y=better_find_positive_triangulation(D,tries)
if Y is not None:
w=better_is_isometric_to(X,Y,index)
if w==True:
BRANCHING_SETS=BRANCHING_SETS+[[knot,slope,DIAGRAM[0],DIAGRAM[1],DIAGRAM[2]]]
for i in range(L.num_cusps()):
if L.cusp_info(i).filling==(0.0, 0.0):
L.dehn_fill((1,0),i)
if L.homology().order() not in homologies:
homologies.append(L.homology().order())
if len(homologies)==3:
return BRANCHING_SETS
return BRANCHING_SETS
start_time = time.time()
knot='o9_30634'
for slope in exc_sym_slopes:
w=search_for_three_branching_sets(knot,slope,index=3,max_crossings=15)
if w==[]:
w=False
if w!=False:
branching_sets=branching_sets+w
print('We found a branching set:',w)
print('Total number of branching sets we have found:', len(branching_sets))
print('Total time taken: %s minutes ---' % ((time.time() - start_time)/60))
We found a branching set: [['o9_30634', (-2, 1), 'L12n1012', (-8, 3), 0], ['o9_30634', (-2, 1), 'L14n23888', (-2, 1), 0], ['o9_30634', (-2, 1), 'L14n24646', (-2, 1), 0]] We found a branching set: [['o9_30634', (-1, 1), 'L10n45', (-5, 1), 0], ['o9_30634', (-1, 1), 'L10n49', (3, 4), 0], ['o9_30634', (-1, 1), 'L11n141', (-3, 1), 0], ['o9_30634', (-1, 1), 'L14a20418', (3, 1), 0], ['o9_30634', (-1, 1), 'L14a20418', (3, 1), 1]] We found a branching set: [['o9_30634', (2, 1), 'L13n9873', (-2, 1), 1]] Total number of branching sets we have found: 10 Total time taken: 164.701756131649 minutes ---
branching_sets
[['o9_30634', (-1, 1), 'K13n2148'], ['o9_30634', (-2, 1), 'L12n1012', (-8, 3), 0], ['o9_30634', (-2, 1), 'L14n23888', (-2, 1), 0], ['o9_30634', (-2, 1), 'L14n24646', (-2, 1), 0], ['o9_30634', (-1, 1), 'L10n45', (-5, 1), 0], ['o9_30634', (-1, 1), 'L10n49', (3, 4), 0], ['o9_30634', (-1, 1), 'L11n141', (-3, 1), 0], ['o9_30634', (-1, 1), 'L14a20418', (3, 1), 0], ['o9_30634', (-1, 1), 'L14a20418', (3, 1), 1], ['o9_30634', (2, 1), 'L13n9873', (-2, 1), 1]]
Since the knot K is strongly invertible we have always at least on way to write the knot as a DBC over a link in S3. by checking that the above surgery links always have the filling knot an unknot we see that we can write the (-1,1)-and the (-2,1)-filling in two more ways as DBC over a link in a lens space. (These two ways are distinguished by the homology of the lens space. This shows that these two slopes can only be written in a single way as the DBC over a link in S3. For the (2,1)-filling we found a surgery description along a link in RP3. Next, we find the remaining description by searching through surgery descriptions along 4 component links with 2 components filled.
import snappy
import time
def double_branched_cover(link):
"""
Returns the double branched covers of the link. This works also for links in a more general manifold.
Note that a knot in a more general manifold may have more than one double branched cover
(or no double branched cover at all if the knot represents a primitive element in homology).
This function will return the complete list of all double branched covers of the link.
"""
L=link.copy()
for i in range(L.num_cusps()):
if L.cusp_info(i).filling==(0.0, 0.0):
L.dehn_fill((2,0),i)
return [cov for cov in L.covers(2) if (2.0, 0.0) not in cov.cusp_info('filling')]
def better_is_isometric_to(X,Y,index=10):
"""
Returns True if X and Y are isometric.
Returns False if X and Y have different homologies.
"""
if X.homology()!=Y.homology():
return False
for i in (0,index):
try:
w=X.is_isometric_to(Y)
if w==True:
return w
except (RuntimeError,snappy.SnapPeaFatalError):
pass
X.randomize()
Y.randomize()
return False
start_time=time.time()
K=snappy.Manifold('K9_449(-12,1)')
slopes=[(0,1),(1,1),(-1,1),(2,1),(-2,1)]
four_cusps=[]
for L in snappy.HTLinkExteriors(knots_vs_links='links',crossings=12,alternating=False):
if L.num_cusps()==4:
four_cusps.append(L)
for L in four_cusps[150:158]:
print(L)
for i in range(4):
for s in slopes:
L.dehn_fill(s,i)
for j in range(4):
if j!=i:
for r in slopes:
L.dehn_fill(r,j)
for C in double_branched_cover(L):
if better_is_isometric_to(K,C):
print('DBC',L,'=',K)
L.dehn_fill((0,0),j)
L.dehn_fill((0,0),i)
print('Seconds taken:',time.time()-start_time)
L12n2160(0,0)(0,0)(0,0)(0,0) L12n2161(0,0)(0,0)(0,0)(0,0) L12n2162(0,0)(0,0)(0,0)(0,0) L12n2163(0,0)(0,0)(0,0)(0,0) DBC L12n2163(0,0)(-2,1)(-2,1)(0,0) = K9_449(-12,1) DBC L12n2163(0,0)(-2,1)(-2,1)(0,0) = K9_449(-12,1) DBC L12n2163(0,0)(0,0)(-2,1)(-2,1) = K9_449(-12,1) DBC L12n2163(0,0)(0,0)(-2,1)(-2,1) = K9_449(-12,1) L12n2164(0,0)(0,0)(0,0)(0,0) L12n2165(0,0)(0,0)(0,0)(0,0) L12n2166(0,0)(0,0)(0,0)(0,0) L12n2167(0,0)(0,0)(0,0)(0,0) Seconds taken: 10.091275930404663
This finishes the classification of the branching sets.